/*
 * A CORBA Generic Server
 * Part of the GICOM project
 */
package gicom.config;

import gicom.GenericObject;
import gicom.GenericServer;
import gicom.PersistentObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Properties;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;

/**
 * provides the configuration of an Object
 * @author Guillaume Missonnier
 */
public class ObjectConfig extends GenericConfig {
    
    /**
     * The Object's POA's id
     */
    private String idPOA;
    
    /**
     * The Object's class
     */
    private String clazz;
    
    /**
     * The Object's iorFileName
     */
    private String iorFileName;
    
    /**
     * The Object's bindingName
     */
    private String bindingName;
    
    /**
     * The Object's codeBase
     */
    private String codeBase;
    
    /**
     * The Object's value
     */
    private GenericObject value;
    
    private boolean isFirstLoad;
    
    public ObjectConfig (String id) throws Exception {
        super("object_", id);
        this.isFirstLoad = false;
        this.load();
    }
    
    public ObjectConfig (String id, String idPOA, String clazz, String iorFileName, String bindingName, String codeBase, GenericObject value) throws Exception{
        super("object_", id);
        this.idPOA = idPOA;
        this.clazz = clazz;
        this.iorFileName = iorFileName;
        this.bindingName = bindingName;
        this.codeBase = codeBase;
        this.value = value;
        this.isFirstLoad = true;
    }
    
    public static void beginTransaction (String transastionId, String objectId) throws Exception {
        if (transastionId.endsWith("_null")) throw new Exception("This transaction id is forbidden.");
        File directory = GenericServer.defaultGenericServer.getDirectory();
        File temp = new File(directory, "object_" + objectId + "_tmp");
        File file = new File(directory, "object_" + objectId);
        File nultrx = new File(directory, "object_" + objectId + "_" + transastionId +"_null_trx");
        File thetrx = new File(directory, "object_" + objectId + "_" + transastionId +"_trx");
        File tmptrx = File.createTempFile("", "");
        if (nultrx.exists() || thetrx.exists()) throw new Exception("This transaction id already exists.");
        if(!file.exists() && !temp.exists()) nultrx.createNewFile();
        else {
            if (!file.exists()) file = temp;
            FileInputStream fis = new FileInputStream(file);
            FileOutputStream fos = new FileOutputStream(tmptrx);
            int value = fis.read();
            while (value != -1) {
                fos.write(value);
                value = fis.read();
            }
            fis.close();
            fos.close();
            tmptrx.renameTo(thetrx);
        }
    }
    
    public static void rollbackTransaction (String transastionId, String objectId) throws Exception {
        File directory = GenericServer.defaultGenericServer.getDirectory();
        File temp = new File(directory, "object_" + objectId + "_tmp");
        File file = new File(directory, "object_" + objectId);
        File nultrx = new File(directory, "object_" + objectId + "_" + transastionId +"_null_trx");
        File thetrx = new File(directory, "object_" + objectId + "_" + transastionId +"_trx");
        if (!nultrx.exists() && !thetrx.exists()) throw new Exception("This transaction id does not exist.");
        else if(!thetrx.exists()) {
            temp.delete();
            file.delete();
            nultrx.delete();
        } else {
            if (file.exists() || temp.exists()) {
                FileInputStream fis;
                FileOutputStream fos;
                if (file.exists()) {
                    fis = new FileInputStream(temp);
                    fos = new FileOutputStream(file);
                } else {
                    fis = new FileInputStream(file);
                    fos = new FileOutputStream(temp);
                }
                int value = fis.read();
                while (value != -1) {
                    fos.write(value);
                    value = fis.read();
                }
                fis.close();
                fos.close();
                thetrx.renameTo(file);
                temp.delete();
            }
        }
    }
    
    public static void commitTransaction (String transastionId, String objectId) throws Exception {
        File directory = GenericServer.defaultGenericServer.getDirectory();
        new File(directory, "object_" + objectId + "_" + transastionId +"_null_trx").delete();
        new File(directory, "object_" + objectId + "_" + transastionId +"_trx").delete();
    }
    
    
    
    @Override
    protected void doSave() throws Exception {
        // Build the properties
        Properties properties = new Properties();
        properties.setProperty("idPOA", getIdPOA());
        properties.setProperty("clazz", getClazz());
        properties.setProperty("iorFileName", getIorFileName()==null?"":getIorFileName());
        properties.setProperty("bindingName", getBindingName()==null?"":getBindingName());
        properties.setProperty("codeBase", getCodeBase()==null?"":getCodeBase());
        // Save the properties
        saveProperties(properties);
    }
    
    @Override
    protected void doLoad() throws Exception {
        Properties properties = loadProperties();
        idPOA = properties.getProperty("idPOA");
        if (getIdPOA() == null) throw new Exception("IdPOA is not define.");
        clazz = properties.getProperty("clazz");
        if (getClazz() == null) throw new Exception("Clazz is not define.");
        bindingName = properties.getProperty("bindingName");
        iorFileName = properties.getProperty("iorFileName");
        codeBase = properties.getProperty("codeBase");
        value = (GenericObject)properties.get("value");
        if (getValue() == null) throw new Exception("Value is not define.");
    }
    
    @Override
    protected void saveProperties (Properties properties) throws Exception {
        File file = new File(GenericServer.defaultGenericServer.getDirectory(), getPrefix() + getId());
        File temp = new File(GenericServer.defaultGenericServer.getDirectory(), getPrefix() + getId() + "_tmp");
        temp.delete();
        temp.createNewFile();
        FileOutputStream fos = new FileOutputStream(temp);
        properties.store(fos, "");
        fos.close();
        writeExternalizedValue(temp, getValue());
        file.delete();
        if (!temp.renameTo(file)) throw new Exception("Error while renaming the temp file.");
    }
    
    @Override
    protected Properties loadProperties () throws Exception {
        File file = new File(GenericServer.defaultGenericServer.getDirectory(), getPrefix() + getId());
        File temp = new File(GenericServer.defaultGenericServer.getDirectory(), getPrefix() + getId() + "_tmp");
        if (!file.exists() && !temp.exists()) throw new Exception("The configuration file " + getPrefix() + getId() + " does not exists.");
        if (!file.exists()) {
            if (!temp.renameTo(file)) throw new Exception("Error while renaming the temp file.");
            temp.delete();
            file = temp;
        }
        Properties properties = new Properties();
        FileInputStream fis = new FileInputStream(file);
        properties.load(fis);
        fis.close();
        ORB orb = null;
        NamingContextExt naming = null;
        try {
            orb = GenericServer.defaultGenericServer.getGenericManager().getPOAORB(properties.getProperty("idPOA"));
            naming = NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));
        } catch (Exception ex2) {

        }
        try {
            properties.put("value", (PersistentObject)readExternalizedValue(file));
        } catch (Exception ex) {
            GenericParameter[] parameters = GenericParameter.parse(properties.getProperty("value"));
            Object[] arguments = new Object[parameters.length];
            Class[] classes = new Class[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                arguments[i] = parameters[i].instanciate(naming);
                classes[i] = arguments[i].getClass();
                if (org.omg.CORBA.portable.ObjectImpl.class.isAssignableFrom(classes[i])) classes[i] = classes[i].getInterfaces()[0];
            }
            properties.put("value", (GenericObject)Class.forName(properties.getProperty("clazz")).getConstructor(classes).newInstance(arguments));
            isFirstLoad = true;
        }
        return properties;
    }
    
    private Object readExternalizedValue (File file) throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream(file);
        byte[] bytes = new byte[6];
        
        bytes[0] = (byte)fis.read();
        if (bytes[0] == -1) throw new IOException("End of file not expected.");
        bytes[1] = (byte)fis.read();
        if (bytes[1] == -1) throw new IOException("End of file not expected.");
        bytes[2] = (byte)fis.read();
        if (bytes[2] == -1) throw new IOException("End of file not expected.");
        bytes[3] = (byte)fis.read();
        if (bytes[3] == -1) throw new IOException("End of file not expected.");
        bytes[4] = (byte)fis.read();
        if (bytes[4] == -1) throw new IOException("End of file not expected.");
        bytes[5] = (byte)fis.read();
        if (bytes[5] == -1) throw new IOException("End of file not expected.");
        
        while (!new String(bytes).equals("value=") && bytes[5] != -1) {
            bytes[0] = bytes[1];
            bytes[1] = bytes[2];
            bytes[2] = bytes[3];
            bytes[3] = bytes[4];
            bytes[4] = bytes[5];
            bytes[5] = (byte)fis.read();
        }
        if (bytes[5] == -1) throw new IOException("End of file not expected.");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object object = ois.readObject();
        ois.close();
        fis.close();
        return object;
    }
    
    private void writeExternalizedValue (File file, Object object) throws FileNotFoundException, IOException {
        FileOutputStream fos = new FileOutputStream (file, true);
        fos.write("value=".getBytes());
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(object);
        fos.write("\n".getBytes());
        oos.close();
        fos.close();
    }

    public String getIdPOA() {
        return idPOA;
    }

    public String getClazz() {
        return clazz;
    }

    public String getIorFileName() {
        return iorFileName;
    }

    public String getBindingName() {
        return bindingName;
    }

    public String getCodeBase() {
        return codeBase;
    }

    public GenericObject getValue() {
        return value;
    }
    
    public boolean isFirstLoad() {
        return isFirstLoad;
    }
}
